<!DOCTYPE html>
<html lang="zh-CN">
    <!---
https://cdn.bootcdn.net/ajax/libs/three.js/0.166.1/three.module.js
比如安装148版本
npm install three@0.148.0 save
-->

    <head>
        <meta charset="UTF-8">
        <title>cube-lee-test</title>
        <script type="importmap">
        {
          "imports": {
            "three": "./js/threejs/three.module.js",
            "three/addons/": "./js/jsm/"
          }
        }
    </script>
        <script src="./js/points.js"></script>
        <script src="./js/pointUtil.js"></script>
        <style>
        html {
            height: 100%;
            user-select: none;
        }

        body {
            margin: 0;
            color: #fff;
            font-family: Monospace;
            font-size: 13px;
            line-height: 24px;
            overscroll-behavior: none;
            height: 100%;
        }

        canvas {
            display: block;
        }

        #tooltip {
            position: absolute;
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 5px;
            border-radius: 5px;
            display: none;
            /* 默认隐藏 */
            pointer-events: none;
            font-size: 12px;
            white-space: break-spaces;
            z-index: 1;
        }

        main {
            /* display: flex; */
            position: relative;
        }

        #panel {
            height: 100%;
            width: 250px;
            transition: all linear 0.2s;
            position: absolute;
            left: 0;
            top: 0;
            background-color: wheat;
            overflow: hidden;
        }

        #panel.hide {
            width: 0;
        }

        #container {
            width: 100%;
            transition: all linear 0s;
        }

        #container.isedit {
            width: calc(100% - 250px);
        }
        #meshs{
            display: flex;
            gap: 5px;
        }
        #meshs .mesh{
            flex: 0 0 30%;
            background-color: blue;
            aspect-ratio: 1/1;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
        }
        #meshs .mesh:nth-child(2){
            background-image: url('./img/p05.png');
            background-size: cover;
        }
        #meshs .mesh:last-child{
            background-color: blueviolet;
        }
        #testdiv{
            width: 100%;
            height: 200px;
            background-color: blueviolet;
        }
    </style>
    </head>

    <body>
        <!-- 创建用于显示提示的文本元素 -->
        <main>
            <div id="panel" class="hide">
                <div id="meshs">
                    <div class="mesh" draggable="true" data-size="100,100"
                        data-value="blue">100x100</div>
                    <div class="mesh" draggable="true" data-size="100,200"
                        data-value="p05.png">100x200</div>
                    <div class="mesh" draggable="true" data-size="100,300"
                        data-value="blueviolet">100x300</div>
                </div>
                <button id="done" style="margin-top: 20px;">完成</button>
                <!-- <div id="testdiv"></div> -->
            </div>
            <div id="container"></div>
        </main>

        <div id="tooltip" onclick="tooltipCkick"></div>
        <script type="module">
        import * as THREE from "./js/threejs/three.module.js";
        import { OrbitControls } from './js/threejs/jsm/controls/OrbitControls.js';
        import { DragControls } from './js/threejs/jsm/controls/DragControls.js'
        import { addCube } from './js/three-material.js';
        // 索引数组，定义每个面由哪些顶点构成 (每个面由两个三角形组成)
        const indicesCube = [
            0, 1, 2, 0, 2, 3, // 底面
            4, 6, 5, 4, 7, 6, // 顶面
            0, 4, 5, 0, 5, 1, // 右面
            1, 5, 6, 1, 6, 2, // 前面
            2, 6, 7, 2, 7, 3, // 上面
            3, 7, 4, 3, 4, 0, // 背面
        ];

        // 一些参数
        let editmode = false;
        const _$ = document.querySelector
        
        let wpWidth = window.innerWidth,wpHeight = window.innerHeight;
        let panelWidth = 0; // 单位 px
        let draggedMesh = null; // 记录拖拽的材质 
        let editObjInfo = {}    //记录当前编辑的控件
        var hCanvas;    // 绘制新材质的画布

        let ltX , ltY;  // 编辑控件左上角的点坐标

        let tempPlane = []; // 存储临时生成的模板模型
        let materialArr = []; //
        let editMesh = null;    // 正在被编辑的物体


        var scene, camera, renderer, container, gridHelper;
        var raycaster = new THREE.Raycaster();
        var pointer = new THREE.Vector2();

        let selectedObject = null;
        let offset = new THREE.Vector3();
        let plane = new THREE.Plane();
        let intersection = new THREE.Vector3();

        let dragControls;
        let orbitControls;
        let initialPositions = new Map();  // 存储物体的初始位置

        initScene();
        render();
        // fetch('http://192.168.9.168:8090/ftmc/sm/sys/menu/listSysMenuPage', {
        //     method: 'POST',
        //     headers:{
        //         "Content-Type":"application/json"
        //     },
        //     body: JSON.stringify({})
        // }).then((response) => response.json()).then(res => {
        //     console.log(res)
        // })

        var hCubes = []
        var hImgs = []
        var optCube = optCubes[0];
        var iCubeNum = optCubes.length
        var iCubeIdx
        for (iCubeIdx = 0; iCubeIdx < iCubeNum; iCubeIdx++) {
            optCube = optCubes[iCubeIdx];
            addCube(camera, scene, optCube, hCubes, hImgs, initialPositions)
        }

        //完成按钮
        document.getElementById('done').addEventListener('click',editDone)

        // 监听拖拽
        Array.from(document.getElementsByClassName('mesh')).forEach(domObj => {
            domObj.addEventListener('dragstart',(event) =>{
                // event.dataTransfer.setData('text/plain', null); 
                draggedMesh = event.target
            })
        })

        renderer.domElement.addEventListener("dragover", (event) => {
            // 阻止默认行为以允许放置
            event.preventDefault();
        });

        // 元素放到场景中
        renderer.domElement.addEventListener('drop', function(event) {
            event.preventDefault();

            pointer.x = (event.clientX / wpWidth) * 2 - 1;
            pointer.y = - (event.clientY / wpHeight) * 2 + 1;
            const raycaster = new THREE.Raycaster();
            raycaster.setFromCamera(pointer, camera);

            const intersects = raycaster.intersectObjects(scene.children);

            if (intersects.length > 0) {
                // 将新的物体放在碰撞点上
                const geoObj = intersects[0].object;
                if(geoObj.type != "GridHelper"){
                    let size = draggedMesh.dataset.size
                    let value = draggedMesh.dataset.value
                    let mWidth = Number(size.split(',')[0])
                    let mHeight = Number(size.split(',')[1])
                    console.log(editObjInfo,geoObj,draggedMesh.dataset)
                    if(editObjInfo.width < mWidth || editObjInfo.height < mHeight){
                        alert('模板尺寸大于控件尺寸!')
                        return
                    }
                    let material
                    if(value.includes('.')){//是图片
                        const planetTexture = new THREE.TextureLoader().load(`./img/${value}`); //创建材质
                        material = new THREE.MeshBasicMaterial( {map: planetTexture, side: THREE.DoubleSide} );
                    }else{
                        material = new THREE.MeshBasicMaterial( {color: value, side: THREE.DoubleSide} );
                    }

                    const planGeometry = new THREE.PlaneGeometry( mWidth, mHeight );
                    
                    const plane = new THREE.Mesh( planGeometry, material );
                    plane.position.set(ltX + mWidth/2, ltY - mHeight/2, editObjInfo.z+1);
                    scene.add( plane );
                    tempPlane.push( plane )
                   
                    let addX = materialArr.reduce((prev,curr) => {
                        return prev + curr.width
                    },0)

                    let picObj = {
                        x: plane.position.x - mWidth/2 - ltX + addX,
                        y: plane.position.y + mHeight/2 - ltY,
                        width: mWidth,
                        height: mHeight,
                        value: value
                    }
                    console.log(plane,picObj)
                    materialArr.push(picObj)
                    ltX += mWidth
                }
                
                // if(){

                // }
                
            }
        });


        // 初始化 DragControls
        dragControls = new DragControls(hCubes, camera, renderer.domElement);

        dragControls.mouseButtons = {
            LEFT: THREE.MOUSE.RIGHT,
            MIDDLE: null,
            RIGHT: null
        }

        // 监听 drag 事件，限制物体只能在 XY 平面上移动
        dragControls.addEventListener('drag', function (event) {
            event.object.position.z = 0;  // 将 Z 坐标固定为 0，确保只在 XY 平面移动
        });

        // 禁用 OrbitControls 在拖动过程中
        dragControls.addEventListener('dragstart', function () {
            orbitControls.enabled = false;  // 禁用 OrbitControls
        });

        // 重新启用 OrbitControls 在拖动结束后
        dragControls.addEventListener('dragend', function () {
            orbitControls.enabled = true;  // 启用 OrbitControls
        });


        //初始化场景
        function initScene() {
            // 创建场景
            scene = new THREE.Scene();
            scene.background = new THREE.Color(0x000000)

            container = document.getElementById('container');
            // 创建相机
            camera = new THREE.PerspectiveCamera(90, wpWidth / wpHeight, 0.1, 10000);
            // camera.position.x = -300;
            camera.position.y = -500;
            camera.position.z += 900;

            // 创建渲染器
            renderer = new THREE.WebGLRenderer({ antialias: true });
            // 渲染器能够渲染阴影效果
            renderer.shadowMap.enabled = true;
            renderer.setSize(wpWidth, wpHeight);
            container.appendChild(renderer.domElement);


            // 添加网格辅助器
            gridHelper = new THREE.GridHelper(5000, 200); // 创建一个200单位的网格，细分为50个方格
            gridHelper.rotation.x = 0.5 * Math.PI
            //gridHelper.position.y = -150;
            scene.add(gridHelper);

            // 方向光
            const directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1);
            directionalLight.position.set(10, 10, 10);

            directionalLight.shadow.mapSize.width = 1024  // default
            directionalLight.shadow.mapSize.height = 1024  // default
            directionalLight.shadow.camera.near = 0.05  // default
            directionalLight.shadow.camera.far = 500  // default
            //将范围调大
            directionalLight.shadow.camera.top = 20 /// 这个是生效的属性
            directionalLight.shadow.camera.right = 20
            directionalLight.shadow.camera.left = -20
            directionalLight.shadow.camera.bottom = -20
            directionalLight.castShadow = true;
            scene.add(directionalLight);

            // 环境光
            const ambientLight = new THREE.AmbientLight(0xffffff, 2);
            scene.add(ambientLight);

            // 添加鼠标控制
            orbitControls = new OrbitControls(camera, renderer.domElement);
            // controls.enableDamping = true; // 使得相机移动更加平滑
            orbitControls.dampingFactor = 0.05; // 阻尼效果

            window.addEventListener( 'mouseup', onPointerUp );
            window.addEventListener('resize', onWindowResize, false);

            window.addEventListener('mousemove', onMouseMove, false);

            // 添加鼠标双击事件，用于将物体还原到初始位置
            window.addEventListener('dblclick', restoreObject);

            window.addEventListener('contextmenu', (event) => {
                editmode && event.preventDefault()
            });
        }

        // 还原物体到拖动前的位置
        function restoreObject(event) {
            if(event.target == document.querySelector('main')){
                return
            }
            pointer.x = (event.clientX / wpWidth) * 2 - 1;
            pointer.y = - (event.clientY / wpHeight) * 2 + 1;

            raycaster.setFromCamera(pointer, camera);

            // 计算物体和射线的焦点
            const intersects = raycaster.intersectObjects(scene.children);
            console.log(intersects)
            hCubes.forEach(object => {
                const initialPosition = initialPositions.get(object);
                if (initialPosition) {
                    object.position.copy(initialPosition);  // 还原到初始位置
                }
            });
        }

        // 编辑区域
        async function toggleEdit(flag,clickedObject) {
            editmode = flag
            _$.call(document,'#panel').classList.toggle('hide')
            _$.call(document,'#container').classList.toggle('isedit')
            panelWidth = flag?0:0
            wpWidth = window.innerWidth - panelWidth
            
            // flag && await sleep(116)

            resetScen(clickedObject)
            
            if (flag) {   // 进入编辑模式

            } else {

            }
        }

        // 进/出 编辑模式
        const resetScen = (clickedObject) => {

            // camera.aspect = wpWidth / wpHeight;
            // camera.updateProjectionMatrix();

            // renderer.setSize(wpWidth, wpHeight);

            if(clickedObject){
                console.log(clickedObject)
                const points = clickedObject.userData.points
                const minX = points[0].x,minY = points[0].y,maxX = points[2].x,maxY = points[2].y
                const centerX = (minX + maxX) / 2,centerY = (minY + maxY) / 2

                const faceWidth = maxX - minX,faceHeight = maxY - minY

                const position = clickedObject.position

                const pX = Number.parseInt(position.x), pY = Number.parseInt(position.y)

                clickedObject.position.x = pX;
                clickedObject.position.y = pY;

                const newX = centerX + pX,newY = centerY + pY

                editObjInfo.x = points[3].x + pX
                editObjInfo.y = points[3].y + pY
                editObjInfo.z =  points[4].z
                editObjInfo.width = faceWidth
                editObjInfo.height = faceHeight

                ltX = editObjInfo.x , ltY = editObjInfo.y;

                camera.position.set(newX, newY, 800)

                // 设置相机看的点 (目标)
                // const target = new THREE.Vector3(newX, newY, position.z);  // 设置目标点为 (0, 0, 0)
                // camera.lookAt(target);
                orbitControls.target.set(newX,newY,position.z)

                orbitControls.update()
                
                editMesh = clickedObject

                // gridHelper.rotation.x = 0.5 * Math.PI
                // gridHelper.rotation.y = 0
                // gridHelper.rotation.z = 0

            }

            orbitControls.enabled = !editmode
            dragControls.enabled = !editmode

        }

        // 编辑完成
        function editDone(){
            toggleEdit(false)
            updateMaterial()
        }

        //更新材质
        function updateMaterial(){
            var hCanvas = document.createElement('canvas');

            let faceWidth = editObjInfo.width
            let faceHeight = editObjInfo.height
                
            hCanvas.width = faceWidth
            hCanvas.height = faceHeight
            hCanvas.style.width = faceWidth + 'px';
            hCanvas.style.height = faceHeight + 'px';
            var hContext = hCanvas.getContext('2d');
            hContext.clearRect(0, 0, faceWidth, faceHeight);
            hContext.fillStyle = '#ffffff';
            hContext.fillRect(0, 0, faceWidth, faceHeight);

            materialArr.forEach(async (formObj) => {
                console.warn(formObj)
                let x = formObj.x,y = formObj.y,width = formObj.width, height = formObj.height,value = formObj.value
                if(value.includes('.')){    // 图片 
                    let  src = `./img/${value}`
                    let img = await loadImage(src)
                    console.log(img)
                    hContext.drawImage(img, x, y, width,height);
                }else{
                    hContext.fillStyle = value;
                    hContext.fillRect(x, y, width,height);
                }
            })
            
            //document.body.appendChild(hCanvas)

            var hTexture = new THREE.Texture(hCanvas);
            hTexture.needsUpdate = true;
            editMesh.material[1] = new THREE.MeshPhongMaterial({ map: hTexture, side: THREE.DoubleSide })
            

            // 清空
            tempPlane.forEach(planeMesh => {
                scene.remove(planeMesh)
            })

            materialArr = []
            editMesh = null
            tempPlane = []
        }

        function loadImage(src) {
            return new Promise((resolve, reject) => {
                const image = new Image();
                image.src = src;
                
                image.onload = () => {
                    resolve(image)
                }
                image.onerror = () => {
                    reject(new Error(`无法加载图像: ${src}`))
                }
            });
        }

        function sleep(ms) {
            return new Promise(resolve => setTimeout(resolve, ms));
        }

        // 右键
        function onPointerUp(event) {
            if(event.button != 2){// 不是右键
                return
            }
            
            // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)

            pointer.x = (event.clientX / wpWidth) * 2 - 1;
            pointer.y = - (event.clientY / wpHeight) * 2 + 1;

            raycaster.setFromCamera(pointer, camera);

            // 计算物体和射线的焦点
            const intersects = raycaster.intersectObjects(scene.children);
            if (intersects.length > 0) {
                const clickedObject = intersects[0].object

                if(clickedObject.position.x != 0 || clickedObject.position.y != 0){ // 元素被移动了
                    !editmode && toggleEdit(true,clickedObject)
                }

                // // 如果点击的物体就是当前选中的物体，则重置位置并退出选中
                // if (clickedObject === selectedObject) {
                //     // 取消选中物体，不进行移动
                //     selectedObject.position.copy(clickedObject.position);
                //     selectedObject = null;
                // } else {
                //     if (clickedObject.userData.data) {
                //         selectedObject = clickedObject;
                //         console.warn(selectedObject)

                //         // 创建一个平面来帮助移动物体
                //         plane.setFromNormalAndCoplanarPoint(
                //             new THREE.Vector3(0, 0, 1),  // 平面的法线垂直于 Z 轴，限制物体在 XY 平面移动
                //             selectedObject.position
                //         );

                //         // 计算选中物体与射线交点的偏移量
                //         if (raycaster.ray.intersectPlane(plane, intersection)) {
                //             offset.copy(intersection).sub(selectedObject.position);
                //         }
                //     }
                // }

                // if( intersectObj.uv ) {
                //     let optItem,faceItem,bcheck=false;

                //     let uvx = intersectObj.uv.x * optparams.size.x;
                //     let uvy = (1-intersectObj.uv.y) * optparams.size.y;

                //     for(let i=0;i<gOptArry.length;i++) {
                //         optItem = gOptArry[i];

                //         if( optItem.faceIdx==intersectObj.face.materialIndex ) {

                //             for(let j=0;j<optItem.size.length;j++) {
                //                 faceItem = optItem.size[j];
                //                 if( faceItem.postion[0].x<=uvx && faceItem.postion[0].y<=uvy &&
                //                     (faceItem.postion[0].x+faceItem.postion[1].x)>=uvx && (faceItem.postion[0].y+faceItem.postion[1].y)>=uvy) {
                //                     bcheck=true;
                //                     console.log('拾取第'+(i+1)+'图片');
                //                     alert(faceItem.title);
                //                     break;
                //                 }
                //             }

                //         }
                //     }

                //     if(!bcheck) {
                //         alert(getDatetime());
                // 	}
                // }

            } else {
                selectedObject = null;
            }

        }

        function onMouseMove(event) {
            // 如果有选中物体，移动它
            if (selectedObject) {
                pointer.x = (event.clientX / wpWidth) * 2 - 1;
                pointer.y = -(event.clientY / wpHeight) * 2 + 1;

                // 更新射线
                raycaster.setFromCamera(pointer, camera);

                // 计算与 XY 平面的交点
                if (raycaster.ray.intersectPlane(plane, intersection)) {
                    // 通过保持初始的 offset 偏移量来更新物体位置
                    selectedObject.position.copy(intersection.sub(offset));
                    // 确保物体只在 XY 平面上移动，Z 坐标固定
                    selectedObject.position.z = 0;  // 固定 Z 坐标为 0
                }
            }
        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth, window.innerHeight);

        }

        function render() {
            // 渲染
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

    </script>

    </body>

</html>